/**
 * @file EncoderProcess.h
 * @author xag (xag@xa.com)
 * @brief
 * @version 1.0.10
 * @date 2023-08-09
 *
 * @copyright Copyright (c) 2007-2023, Guangzhou Xaircraft Technology Co., Ltd.
 *
 */
#pragma once
#include <linux/videodev2.h>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <thread>
#include "CycleBuffer.h"

//using namespace kapok_common_basic;     // NOLINT

#define MAX_CAPBUFS 2
#define MAX_OUTBUFS 2
#define MAX_PLANES 3
#define ALIGN(x, a) (((x) + (a) - 1L) & ~((a) - 1L))
#define HW_ALIGN 64

struct tienc_buffer {
    void *mapped;
    int offset;
    int length;
    unsigned int bo_handle;
    int dbuf_fd;
};

/*
 * struct tienc_format: Describes formats
 *
 * name: Name of format for finding
 * type: V4L2 type of format (input/output)
 * fourcc: V4L2 fourcc
 * size_num: Numerator for image buffer size calculation
 * size_den: Denominator for image buffer size calculation
 * n_planes: Number of buffer planes
 * width: Image width
 * height: Image height
 * stride: Image stride
 * size: Calculated size of image data
 * bytes_pp: Number of bytes per pixel
 * memory: Type of memory to use
 */
struct tienc_format {
    char *name;
    uint32_t type;
    uint32_t fourcc;
    int size_num;
    int size_den;
    int n_planes;
    int width;
    int height;
    int stride;
    unsigned int size;
    int bytes_pp;
    enum v4l2_memory memory;
};

typedef struct tda4_encode {
    int fd;
    int rdfd;
    int wrfd;
    int n_outbuffers;
    int n_capbuffers;
    int n_frames;
    struct tienc_buffer outbuffers[MAX_OUTBUFS];
    struct tienc_buffer capbuffers[MAX_CAPBUFS];
    struct tienc_format out_format = {0};
    struct tienc_format cap_format = {0};
    char *yuv_data;
    char *enc_stream;
    int *stream_len;
} ENCODE_INFO_T;

namespace kapok_hardware_tiencoder {

class EncoderProcess {
     public: /* NOLINT */
        EncoderProcess();
        ~EncoderProcess();

        int EncoderInit(int vwidth, int vheight);
        int EncoderYUV(char *yuv, char *stream, int *stream_len);

     private: /* NOLINT */
        int HandleOutbuf(int fd, int rdfd, int index,
             struct tienc_buffer buffer,
             struct tienc_format format,
             int n_frames,
             char *yuv_data);

        int HandleCapbuf(int fd, int wrfd, int index,
             struct tienc_buffer buffer,
             struct tienc_format format,
             int write_length,
             char *stream,
             int *stream_len);

        int QueueBuffer(int fd, int index, struct tienc_buffer buffer,
            struct tienc_format format);

        int StreamingLoop(void * ptr_encode_info);

        int CreateMmapBuffer(int fd, struct tienc_buffer *b,
                        enum v4l2_buf_type type, int index,
                        struct tienc_format format);

        int AllocBufs(int fd, int drmfd, int *n_buffers,
                    struct tienc_buffer buffers[], enum v4l2_buf_type type,
                    struct tienc_format format);

        void DeallocBufs(int fd, int drmfd, int *n_buffers, struct tienc_buffer buffers[],
             struct tienc_format format);

        void PrintV4l2Format(struct v4l2_format *fmt);

        void GetFormat(int fd, struct tienc_format format);

        int TryFormat(int fd, struct tienc_format format);

        int SetFormat(int fd, struct tienc_format format);

        void GetParameters(int fd);

        int SetParameters(int fd, int num, int den);

        void QueryCtrls(int fd);

        void GetCtrls(int fd);

        void TryCtrls(int fd, int bitrate, int gop_size, int i_period);

        void SetCtrls(int fd, int bitrate, int gop_size, int i_period);

        int SubscribeEvents(int fd);

        void UnsubscribeEvents(int fd);

        int QueryDevice(int fd);

        int CheckFramerate(int fd, int *frameival_num, int *frameival_den);

        int GetNFrames(int rdfd, int frame_size);

        void FindFormat(char name[], struct tienc_format *format,
            const struct tienc_format formats[], int formats_size);

        void CalculateFormats(char format_name[], char codec_name[],
                     struct tienc_format *out_format,
                     struct tienc_format *cap_format,
                     int width, int height);

        int FindDevice(char *dev_path);

     private: /* NOLINT */
        // CycleBuffer m_CycleBuffer;

    char output_file[256] = "video.h264";
#ifdef DRMMODE
    const char *default_drm_path = "/dev/dri/";
#endif
    const char *default_dev_path = "/dev/";
    char dev_path[256] = "";
    char dev_name[256] = "/dev/video1";
    char drm_path[256] = "";
    char drm_name[256] = "";
    char format_name[8] = "NV12";
    char codec_name[8] = "H264";
    int width = 0, height = 0;
    int drmfd = -1;
    int bitrate = 2000000, gop_size = 30, i_period = 30;
    int frameival_num = 1, frameival_den = 30;
    enum v4l2_memory out_memory = V4L2_MEMORY_MMAP;
    enum v4l2_memory cap_memory = V4L2_MEMORY_MMAP;
    ENCODE_INFO_T encode_info = {0};
};

}    // namespace kapok_hardware_tiencoder
